#include "./ble_uart.h"
#include "../device/gpio.h"
#include "../device/dma.h"
#include "../rtos/event_flags.h"
#include <string.h>

namespace driver {

using namespace device;

using TX = PD<5>; // DMA1_Stream6_Channel4
using RX = PD<6>; // DMA1_Stream5_Channel4
using STATE = PD<8>;
using EN = PD<7>;

using TXDMA = Dma<1, 6>;
static constexpr uint8_t TXChan = 4;
#define TX_STREAM   DMA1_Stream6

using RXDMA = Dma<1, 5>;
static constexpr uint8_t RXChan = 4;
#define RX_STREAM   DMA1_Stream5

BLEUart::BLEUart() {

    RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN + RCC_AHB1ENR_DMA1EN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;

    ConfigAf<7, TX, RX>();
    ConfigOD<EN>();
    ConfigUpInput<STATE>();

    USART2->CR1 = 0x00;
    USART2->CR2 = 0x00;
    USART2->CR3 = 0xC0;
    USART2->BRR = 42000000 / 9600;
    USART2->CR1 = 0x201C;

    RXDMA::ClrFlag();
    RX_STREAM->CR = 0x00;
    RX_STREAM->M0AR = reinterpret_cast<uint32_t>(m_recv_buf);
    RX_STREAM->PAR = reinterpret_cast<uint32_t>(&USART2->DR);
    RX_STREAM->NDTR = RecvBuffSize;
    RX_STREAM->CR = (RXChan << 25) +
        (0x01 << 10) +
        (0x01 << 8) +
        (0x01);

    NVIC_EnableIRQ(DMA1_Stream6_IRQn);
    NVIC_EnableIRQ(USART2_IRQn);

    open();
}

void BLEUart::open() {
    EN::Clr();
}

void BLEUart::close() {
    EN::Set();
}

void BLEUart::write_by_dma(const void *buf, uint32_t len) {
    TXDMA::ClrFlag();
    TX_STREAM->CR = 0x00;
    TX_STREAM->M0AR = reinterpret_cast<uint32_t>(buf);
    TX_STREAM->PAR = reinterpret_cast<uint32_t>(&USART2->DR);
    TX_STREAM->NDTR = len;
    TX_STREAM->CR = (TXChan << 25) +
        (0x01 << 10) +
        (0x01 << 6) +
        (0x01 << 4) +
        (0x01);
}

void BLEUart::wait_write_complete() {
    rtos::flags::global->wait_any(rtos::flags::BLEUartTxFlag);
}

void BLEUart::on_recv_idle() {
    uint32_t index = RecvBuffSize - RX_STREAM->NDTR;
    if (index == m_recv_index) {
        return;
    }
    utils::byte_view_t bw;
    if (index > m_recv_index) {
        bw.buf = m_recv_buf + m_recv_index;
        bw.len = index - m_recv_index;
    } else {
        uint32_t k = RecvBuffSize - m_recv_index;
        memcpy(m_copy_buf, m_recv_buf + m_recv_index, k);
        memcpy(m_copy_buf + k, m_recv_buf, index);
        bw.buf = m_copy_buf;
        bw.len = k + index;
    }
    m_recv_index = index;
    m_mq.post(bw);
}

}

using namespace driver;

extern "C"
void DMA1_Stream6_IRQHandler() {
    if (!TXDMA::IsTC()) {
        return;
    }
    TXDMA::ClrFlag();
    rtos::flags::global->set(rtos::flags::BLEUartTxFlag);
}

extern "C"
void USART2_IRQHandler() {
    if (USART2->SR & USART_SR_IDLE) {
        uint32_t clr = USART2->DR;
        bleUart->on_recv_idle();
    }
}


